package com.diploma.project.aiqiyi.utils;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ValidatorUtils {

    private static final String BASE_CODE_STRING = "0123456789ABCDEFGHJKLMNPQRTUWXY";
    private static final char[] BASE_CODE_ARRAY = BASE_CODE_STRING.toCharArray();
    private static final List<Character> BASE_CODES = new ArrayList<>();
    private static final String BASE_CODE_REGEX = "[" + BASE_CODE_STRING + "]{18}";
    private static final int[] WEIGHT = {1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28};

    public static Hashtable CARD_PROVINCE = new Hashtable();

    public static int USER_MIN_AGE = 16;

    public static int USER_MAX_AGE = 60;

    private static Validator validator;

    static {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();

        for (char c : BASE_CODE_ARRAY) {
            BASE_CODES.add(c);
        }
    }

    /**
     * JSR303手动校验
     *
     * @param object
     * @param groups
     * @param <T>
     */
    public static <T> void validator(T object, Class... groups) {
        Set<ConstraintViolation<T>> violations = validator.validate(object, groups);
        int errSize = violations.size();

        if (errSize == 0) return;
//        String[] errMsg = new String[errSize];
//        boolean result = true;
//        if (errSize > 0) {
//            int i = 0;
//            for (ConstraintViolation<T> violation : violations) {
//                errMsg[i] = violation.getMessage();
//                i++;
//            }
//            result = false;
//        }
        throw new ConstraintViolationException(violations);
    }

    static {

        CARD_PROVINCE.put("11", "北京");
        CARD_PROVINCE.put("12", "天津");
        CARD_PROVINCE.put("13", "河北");
        CARD_PROVINCE.put("14", "山西");
        CARD_PROVINCE.put("15", "内蒙古");
        CARD_PROVINCE.put("21", "辽宁");
        CARD_PROVINCE.put("22", "吉林");
        CARD_PROVINCE.put("23", "黑龙江");
        CARD_PROVINCE.put("31", "上海");
        CARD_PROVINCE.put("32", "江苏");
        CARD_PROVINCE.put("33", "浙江");
        CARD_PROVINCE.put("34", "安徽");
        CARD_PROVINCE.put("35", "福建");
        CARD_PROVINCE.put("36", "江西");
        CARD_PROVINCE.put("37", "山东");
        CARD_PROVINCE.put("41", "河南");
        CARD_PROVINCE.put("42", "湖北");
        CARD_PROVINCE.put("43", "湖南");
        CARD_PROVINCE.put("44", "广东");
        CARD_PROVINCE.put("45", "广西");
        CARD_PROVINCE.put("46", "海南");
        CARD_PROVINCE.put("50", "重庆");
        CARD_PROVINCE.put("51", "四川");
        CARD_PROVINCE.put("52", "贵州");
        CARD_PROVINCE.put("53", "云南");
        CARD_PROVINCE.put("54", "西藏");
        CARD_PROVINCE.put("61", "陕西");
        CARD_PROVINCE.put("62", "甘肃");
        CARD_PROVINCE.put("63", "青海");
        CARD_PROVINCE.put("64", "宁夏");
        CARD_PROVINCE.put("65", "新疆");
        CARD_PROVINCE.put("71", "台湾");
        CARD_PROVINCE.put("81", "香港");
        CARD_PROVINCE.put("82", "澳门");
        CARD_PROVINCE.put("91", "国外");
    }

    /**
     * 中国公民身份证号码最小长度。
     */
    public final int CHINA_ID_MIN_LENGTH = 15;

    /**
     * 中国公民身份证号码最大长度。
     */
    public final int CHINA_ID_MAX_LENGTH = 18;

    /**
     * 根据身份编号获取年龄
     *
     * @param idCard 身份编号
     * @return 年龄
     */
    public static int getAgeByIdCard(String idCard) {
        int iAge = 0;
        Calendar cal = Calendar.getInstance();
        String year = idCard.substring(6, 10);
        int iCurrYear = cal.get(Calendar.YEAR);
        iAge = iCurrYear - Integer.valueOf(year);
        return iAge;
    }

    public static int getAge(String idCard, Date date) {
        Date birthDay = null;
        try {
            birthDay = new SimpleDateFormat("yyyyMMdd").parse(idCard.substring(6, 14));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        if (cal.before(birthDay)) { //出生日期晚于当前时间，无法计算
            throw new IllegalArgumentException("The birthDay is before Now. It's unbelievable!");
        }
        int yearNow = cal.get(Calendar.YEAR);  //当前年份
        int monthNow = cal.get(Calendar.MONTH);  //当前月份
        int dayOfMonthNow = cal.get(Calendar.DAY_OF_MONTH); //当前日期
        cal.setTime(birthDay);
        int yearBirth = cal.get(Calendar.YEAR);
        int monthBirth = cal.get(Calendar.MONTH);
        int dayOfMonthBirth = cal.get(Calendar.DAY_OF_MONTH);
        int age = yearNow - yearBirth;   //计算整岁数
        if (monthNow <= monthBirth) {
            if (monthNow == monthBirth) {
                if (dayOfMonthNow < dayOfMonthBirth) age--;//当前日期在生日之前，年龄减一
            } else {
                age--;//当前月份在生日之前，年龄减一
            }
        }
        return age;
    }

    /**
     * 根据身份编号获取生日
     *
     * @param idCard 身份编号
     * @return 生日(yyyyMMdd)
     */
    public static String getBirthByIdCard(String idCard) {
        return idCard.substring(6, 14);
    }

    /**
     * 根据身份编号获取生日年
     *
     * @param idCard 身份编号
     * @return 生日(yyyy)
     */
    public static Short getYearByIdCard(String idCard) {
        return Short.valueOf(idCard.substring(6, 10));
    }

    /**
     * 根据身份编号获取生日月
     *
     * @param idCard 身份编号
     * @return 生日(MM)
     */
    public static Short getMonthByIdCard(String idCard) {
        return Short.valueOf(idCard.substring(10, 12));
    }

    /**
     * 根据身份编号获取生日天
     *
     * @param idCard 身份编号
     * @return 生日(dd)
     */
    public static Short getDateByIdCard(String idCard) {
        return Short.valueOf(idCard.substring(12, 14));
    }

    /**
     * 根据身份编号获取性别
     *
     * @param idCard 身份编号
     * @return 性别(M - 男 ， F - 女 ， N - 未知)
     */
    public static String getGenderByIdCard(String idCard) {
        String sGender = "未知";

        String sCardNum = idCard.substring(16, 17);
        if (Integer.parseInt(sCardNum) % 2 != 0) {
            sGender = "1";//男
        } else {
            sGender = "2";//女
        }
        return sGender;
    }

    public static boolean chekIdCard(String id, int sex) {
        int iW[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
        String szVerCode[] = new String[]{"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};

        // 身份证 位数校验
        if (id == null || "".equals(id) || (id.length() != 18 && id.length() != 15)) {
            return false;
        }

        String Ai = "";
        if (id.length() == 18) {
            Ai = id.substring(0, 17);

        } else if (id.length() == 15) {
            Ai = id.substring(0, 6) + "19" + id.substring(6, 15);
        }

        // 数字校验 前17位全部数字
        if (!RegexUtil.match(RegexUtil.NUM_REG, Ai)) {
            return false;
        }

        // 身份证省份编码判断
        if (CARD_PROVINCE.get(Ai.substring(0, 2)) == null) {
            return false;
        }

        // 性别校验
        if (sex != -1) {
            int sexNum = Integer.parseInt(Ai.substring(15)) % 2;
            if (sexNum != sex) {
                return false;
            }
        }

        char pszSrc[] = Ai.toCharArray();

        int iS = 0;
        for (int i = 0; i < 17; i++) {
            iS += (int) (pszSrc[i] - '0') * iW[i];
        }
        int iY = iS % 11;
        if (id.length() == 18) {
            return szVerCode[iY].equalsIgnoreCase(id.substring(17));
        }
        return true;
    }

    /**
     * 手机号验证
     *
     * @param phone
     * @return 验证通过返回true
     */
    public static boolean isMobile(String phone) {
        if (StringUtil.isBlank(phone)) {
            return false;
        }

        String regex = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
        if (phone.length() != 11) {
            return false;
        } else {
            Pattern p = Pattern.compile(regex);
            Matcher m = p.matcher(phone);
            boolean isMatch = m.matches();
            return isMatch;
        }
    }

    /**
     * 校验统一社会信用代码/营业执照注册号/组织机构代码
     *
     * @param businessLicense
     * @return
     */
    public static boolean isBusinessLicense(String businessLicense) {
        if (StringUtil.isBlank(businessLicense) || !(businessLicense.length() == 15 || businessLicense.length() == 18))
            return false;
        if (businessLicense.length() == 15)
            return isBusinessLicense15(businessLicense);
        if (businessLicense.length() == 18)
            return isValidSocialCreditCode(businessLicense);
        return false;
    }

    /**
     * 校验营业执照注册号（15位）
     *
     * @param businessLicense
     * @return
     */
    public static boolean isBusinessLicense15(String businessLicense) {
        String businesslicensePrex14 = businessLicense.substring(0, 14);// 获取营业执照注册号前14位数字用来计算校验码
        String businesslicense15 = businessLicense.substring(14, businessLicense.length());// 获取营业执照号的校验码
        char[] chars = businesslicensePrex14.toCharArray();
        int[] ints = new int[chars.length];
        for (int i = 0; i < chars.length; i++) {
            ints[i] = Integer.parseInt(String.valueOf(chars[i]));
        }
        if (businesslicense15.equals(getCheckCode(ints) + "")) {// 比较 填写的营业执照注册号的校验码和计算的校验码是否一致
            return true;
        }
        return false;
    }

    /**
     * 营业执照 统一社会信用代码（18位）
     *
     * @param businessLicense
     * @return
     */
    public static boolean isBusinessLicense18(String businessLicense) {
        String regex = "^([159Y]{1})([1239]{1})([0-9ABCDEFGHJKLMNPQRTUWXY]{6})([0-9ABCDEFGHJKLMNPQRTUWXY]{9})([0-90-9ABCDEFGHJKLMNPQRTUWXY])$";
        if (!businessLicense.matches(regex)) {
            return false;
        }
        String str = "0123456789ABCDEFGHJKLMNPQRTUWXY";
        int[] ws = {1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28};
        String[] codes = new String[2];
        codes[0] = businessLicense.substring(0, businessLicense.length() - 1);
        codes[1] = businessLicense.substring(businessLicense.length() - 1, businessLicense.length());
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            sum += str.indexOf(codes[0].charAt(i)) * ws[i];
        }
        int c18 = 31 - (sum % 31);
        if (c18 == 31) {
            c18 = 'Y';
        } else if (c18 == 30) {
            c18 = '0';
        }
        if (str.charAt(c18) != codes[1].charAt(0)) {
            return false;
        }
        return true;
    }

    /**
     * 获取 营业执照注册号的校验码
     *
     * @param ints
     * @return
     */
    private static int getCheckCode(int[] ints) {
        if (null != ints && ints.length > 1) {
            int ti = 0;
            int si = 0; // pi|11+ti
            int cj = 0; // （si||10==0？10：si||10）*2
            int pj = 10; // pj=cj|11==0?10:cj|11
            for (int i = 0; i < ints.length; i++) {
                ti = ints[i];
                pj = (cj % 11) == 0 ? 10 : (cj % 11);
                si = pj + ti;
                cj = (0 == si % 10 ? 10 : si % 10) * 2;
                if (i == ints.length - 1) {
                    pj = (cj % 11) == 0 ? 10 : (cj % 11);
                    return pj == 1 ? 1 : 11 - pj;
                }
            }
        }
        return -1;
    }

    /**
     * 是否是有效的统一社会信用代码
     *
     * @param socialCreditCode 统一社会信用代码
     * @return
     */
    public static boolean isValidSocialCreditCode(String socialCreditCode) {
        if (StringUtil.isBlank(socialCreditCode) || !Pattern.matches(BASE_CODE_REGEX, socialCreditCode)) {
            return false;
        }
        char[] businessCodeArray = socialCreditCode.toCharArray();
        char check = businessCodeArray[17];
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            char key = businessCodeArray[i];
            sum += (BASE_CODES.indexOf(key) * WEIGHT[i]);
        }
        int value = 31 - sum % 31;
        return check == BASE_CODE_ARRAY[value % 31];
    }

}